/*
 * archivos.c
 *
 *  Created on: 27/11/2011
 *      Author: Normandia
 */

#include "archivos.h"

void escribirArchivo(uint64_t fh, size_t size, off_t offset, void* buf){
        void* buffer;
        uint32_t n_cluster, i; // Número de cluster a leer
        size_t n_bytes; // Bytes a leer del buffer
        off_t pos = 0; // Posición
        OPENFILE* openTD = (OPENFILE*)fh;
        uint32_t cache_max = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        if(offset+size > openTD->size) {

        	// El archivo modifica su tamaño -> hay que cambiar la tabla de directorio
        	openTD->size = offset+size;
        	openTD->modificado = 1;
        }

        n_cluster = obtenerNCluster(openTD->primerCluster, offset / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE));

        offset = offset % (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        while(size>0){

        	if(n_cluster >= 0x0FFFFFF0)
        		n_cluster = reservarCluster(openTD->primerCluster);

        	buffer = leerClusterEnCache(fh,n_cluster);
        	if(buffer == NULL ){
        		buffer = leerCluster(n_cluster);
        		agregarCache(buffer,fh,n_cluster);
        	}

			n_bytes = (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE) - offset;
			n_bytes = n_bytes > size ? size : n_bytes;

			//memcpy(ret+pos, buffer + offset, n_bytes);
			memcpy(buffer+offset, buf+pos, n_bytes);

			if(cache_max == 0)
				escribirCluster(n_cluster,buffer);

			for (i=0;i<cache_max;i++){
				if(openTD->cache[i].Cluster == n_cluster) {
					openTD->cache[i].modificado = 1;
					break;
				}
			}

			pos += n_bytes;
			offset = 0;
			size -= n_bytes;

			if(size > 0) {
				// Obtener un cluster libre y actualizar la fat
				n_cluster = obtenerSigCluster(n_cluster);
			}

			if (cache_max == 0)
				free(buffer);
        }

}

void cerrarArchivo(uint64_t fh){

        OPENFILE* tdArchivo = (OPENFILE*) fh;
        DIRECTORYTABLE* directorio;
        uint32_t cache_max = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);
        uint32_t i;

        //tdArchivo->vecesAbierto--;

        if (tdArchivo->vecesAbierto < 1) { // El archivo cerró su última apertura. Se saca de la lista

        	for(i=0;i<cache_max;i++){

        		if (tdArchivo->cache[i].buf != NULL){
					if(tdArchivo->cache[i].modificado == 1){
						escribirCluster(tdArchivo->cache[i].Cluster,tdArchivo->cache[i].buf);

					}
					free(tdArchivo->cache[i].buf);

				}

        	}

        	// Modifica la TD si el archivo fue modificado
        	if(tdArchivo->modificado){

        		directorio = (DIRECTORYTABLE*)leerCluster(tdArchivo->dirCluster);
        		directorio[tdArchivo->idTD].fileSize = tdArchivo->size;
        		memcpy(&directorio[tdArchivo->idTD].low2Bytes,&tdArchivo->primerCluster,2);
        		memcpy(&directorio[tdArchivo->idTD].high2Bytes,((void*)(&tdArchivo->primerCluster))+2,2);
        		escribirCluster(tdArchivo->dirCluster,(void*)directorio);
        		free(directorio);

        		escribirFat();

        	}

        	collection_list_removeByPointer(listOpenFile, (void*)tdArchivo, free);
        }

}

void truncarArchivo(uint64_t fi,size_t size){

	OPENFILE* archivoTD = (OPENFILE*) fi;
	uint32_t cluster, ultimo_cluster;
	uint32_t i,aux;

	if(size < archivoTD->size){ // Hace más chico el archivo

		aux = size / (SECTOR_SIZE * cache_boot_sector->n_sectors_per_cluster);

		cluster = archivoTD->primerCluster;
		for(i=0;i<aux;i++)
			cluster = obtenerSigCluster(cluster); // Obtenemos el último cluster del archivo

		ultimo_cluster = cluster;

		// Elimina los clusters sobrantes
		if(cache_fat[cluster]<0x0FFFFFF0){
			cluster = obtenerSigCluster(cluster);
			do {
				aux = cluster;
				agregarClusterLibre(cluster);
				cluster = obtenerSigCluster(cluster);
				cache_fat[aux] = 0;
			}while(cluster<0x0FFFFFF0);
		}

		if(size == 0){
			cache_fat[ultimo_cluster] = 0;
			agregarClusterLibre(ultimo_cluster);
			archivoTD->primerCluster = 0;
		}else
			cache_fat[ultimo_cluster] = CLUSTER_EOF;

	}else{ // Hace más grande el archivo

		if(archivoTD->primerCluster == 0)
			archivoTD->primerCluster = reservarCluster(0);

		aux = (size-archivoTD->size)  / (SECTOR_SIZE * cache_boot_sector->n_sectors_per_cluster);
		for(i=0;i<aux;i++)
			reservarCluster(archivoTD->primerCluster); // Reserva los clusters que faltan para el archivo
	}

	// Cambia el tamaño del archivo
	archivoTD->size = size;
	archivoTD->modificado = 1;

}
